using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace Xacc
{
	partial class PropertyGrid
	{
		abstract class GridControl : CompositeControl
		{
			PropertyGrid m_parentGrid;

			public PropertyGrid ParentGrid {
				get {

					if (null == m_parentGrid) {
						Control p = Parent;

						while (!(p is PropertyGrid)) {
							p = p.Parent;
						}

						m_parentGrid = p as PropertyGrid;
					}

					return m_parentGrid;
				}
			}

			protected string Res(string resourceKey)
			{
				return ParentGrid.Res(resourceKey);
			}
		}

		class PropertyGridHeader : GridControl
		{
			protected override void Render(HtmlTextWriter writer)
			{
				writer.AddAttribute(HtmlTextWriterAttribute.Class, "PGH");
				writer.RenderBeginTag(HtmlTextWriterTag.Div);
				writer.Write("<a href='#' class='up' title='{0}'></a>", xacc.Properties.Resources.title_ExpandAll);
				writer.Write("<a href='#' class='down' title='{0}'></a>", xacc.Properties.Resources.title_CollapseAll);
				writer.Write("<a href='#' class='{1}' title='{0}'></a>", xacc.Properties.Resources.title_ToggleHelp, ParentGrid.ShowHelp ? "help" : "nohelp");
				writer.RenderEndTag();
			}
		}

		class PropertyGridFooter : GridControl
		{
			protected override void Render(HtmlTextWriter writer)
			{
				writer.Write(@"<div class='footer'{1}><dl><dt>An ASP.NET PropertyGrid</dt><dd>(c) 2006 leppie</dd></dl></div>",
						ClientID,
						ParentGrid.ShowHelp
							? string.Empty
							: " style='display:none'");

				writer.Write(@"
<div	class='status'>
	<img class='busy hidden' title='Busy...' alt='BUSY'/>
	<img class='readonly{0}' title='Values cannot be modified' alt='LOCK' />
	<a href='http://codeplex.com/PropertyGrid' class='information' title='Visit project site' alt='INFO'></a>
</div>", ParentGrid.ReadOnly ? string.Empty : " hidden");
			}
		}

		class PropertyGridCategory : CompositeControl
		{
			public string CategoryName { get; set; }

			protected override void Render(HtmlTextWriter writer)
			{
				writer.Write(@"
<div class='PGC'>
<div class='PGC_OPEN'></div>
<div class='PGC_HEAD'><span><a href='#cat{1}'>{0}</a></span></div>
<div id='{1}' class='PGC_WRAP'>", this.CategoryName, ClientID);

				this.RenderChildren(writer);
				writer.Write(@"</div></div>");
			}

			internal IEnumerable<PropertyGridItem> Items {
				get {
					return this.Controls.OfType<PropertyGridItem>();
				}
			}
		}

		class PropertyGridSubItem : PropertyGridItem
		{
			readonly PropertyGridItem ParentItem;

			public PropertyGridSubItem(PropertyDescriptor pd, PropertyGridItem parentitem)
				: base(pd)
			{
				this.ParentItem = parentitem;
			}

			public override object SelectedObject {
				get { return ParentItem.Descriptor.GetValue(base.SelectedObject); }
			}

			protected override bool IsSubItem { get { return true; } }
		}

		class SubItems : HtmlGenericControl
		{
			internal SubItems()
				: base("div")
			{
				this.Style.Add(HtmlTextWriterStyle.Display, "none");
			}
		}

		class PropertyGridItem : GridControl
		{
			#region Fields
			internal readonly PropertyDescriptor Descriptor;
			#endregion Fields

			#region Constructors
			
			public PropertyGridItem(PropertyDescriptor propdesc)
			{
				this.Descriptor = propdesc;
			}
			
			#endregion Constructors

			#region Properties

			protected virtual bool IsSubItem { get { return false; } }

			internal IEnumerable<PropertyGridSubItem> Items {
				get {
					var _container = this.Controls.OfType<SubItems>().FirstOrDefault();

					return
						null == _container
							? Enumerable.Empty<PropertyGridSubItem>()
							: _container.Controls.OfType<PropertyGridSubItem>();
				}
			}

			public string Name { get { return Descriptor.Name; } }

			/// <summary>
			/// Original value from 
			/// </summary>
			protected string PropertyValue {
				get {
					return Descriptor.GetValueAsString(SelectedObject);
				}
			}

			Func<string> ValueGetter;

			public string Value {
				get {
					return null != this.ValueGetter ? this.ValueGetter() : string.Empty;
				}
			}

			public virtual object SelectedObject {
				get { return this.ParentGrid.SelectedObject; }
			}

			#endregion Properties

			#region Methods
			//for the whole patter implementation consult System.Web.UI.WebControls.SiteMapPath
			protected override void CreateChildControls()
			{
				this.Controls.Clear();
				this.CreateControlHierarchy();
				this.ClearChildState();
			}

			protected void CreateControlHierarchy()
			{
				SubItems _subItemsContainer = null;

				if (Descriptor.Converter.GetPropertiesSupported()) {
					_subItemsContainer = new SubItems();
					this.Controls.Add(_subItemsContainer);

					(from _spd in Descriptor.Converter.GetProperties(Descriptor.GetValue(this.SelectedObject)).Cast<PropertyDescriptor>()
					 where _spd.IsBrowsable
					 select new PropertyGridSubItem(_spd, this)
					).ToList().ForEach(_subItemsContainer.Controls.Add);
				}

				var _lines = new List<string>();

				_lines.Add("<div class='PGI");

				if (this.IsSubItem) {
					_lines.Add(" sub");//for indentation
				}

				if (this.Descriptor.IsReadOnly) {
					_lines.Add(" readonly");//gray out
				}

				_lines.Add("'>");

				_lines.Add(_subItemsContainer != null
					? "<a href='#' class='PGI_CLOSED'>&nbsp;</a>"
					: "<div class='PGI_NONE'></div>");

				_lines.Add("<dt>");

				this.Controls.Add(new LiteralControl(string.Join(string.Empty, _lines.ToArray())));

				var _label = new Label {
					Text = Descriptor.DisplayName
				};

				this.Controls.Add(_label);
				this.Controls.Add(new LiteralControl("</dt>"));

				var _editorContainer = new HtmlGenericControl("div");
				_editorContainer.Attributes.Add("class", "editor");
				var _editor = this.CreateEditor();
				_editorContainer.Controls.Add(_editor);
				_editor.PreRender += (_s, _e) => {
					_label.AssociatedControlID = _editor.ID;
				};

				this.Controls.Add(_editorContainer);

				if (!string.IsNullOrEmpty(this.Descriptor.Description)) {
					this.Controls.Add(new LiteralControl("<dfn style='display:none'>" + this.Descriptor.Description + "</dfn>"));
				}

				this.Controls.Add(new LiteralControl("</div>"));

				if (null != _subItemsContainer) {
					this.Controls.Add(_subItemsContainer);
				}
			}

			protected virtual Control CreateEditor()
			{
				if (this.Descriptor.IsReadOnly || this.ParentGrid.ReadOnly) {
					var _ctl =
						new Label {
							Text = this.PropertyValue,
							Enabled = false,
							ToolTip = this.PropertyValue,
						};
					
					this.ValueGetter = () => _ctl.Text;
					
					return _ctl;
				} else {
					TypeConverter tc = Descriptor.Converter;

					if (tc.GetStandardValuesSupported()) {
						string pv = this.PropertyValue;
						DropDownList _ctl = new DropDownList();

						_ctl.Items.AddRange(
							(from _si in tc.GetStandardValues().Cast<object>()
							 let _val = tc.ConvertToString(_si)
							 select new ListItem {
								 Text = _val,
								 Value = _val,
							 }).ToArray());

						_ctl.SelectedValue = pv;

						this.ValueGetter = () => _ctl.SelectedValue;

						return _ctl;

					} else {
						if (tc.CanConvertFrom(typeof(string))) {

							var _ctl = new TextBox {
								Text = this.PropertyValue,
							};

							this.ValueGetter = () => _ctl.Text;

							return _ctl;
						} else {
							var _ctl = new Label {
								Text = this.PropertyValue,
								Enabled = false,
								ToolTip = this.PropertyValue,
							};

							this.ValueGetter = () => _ctl.Text;

							return _ctl;
						}
					}
				}
			}

			public override void DataBind()
			{
				this.OnDataBinding(EventArgs.Empty);
			}

			protected override void OnDataBinding(EventArgs e)
			{
				base.OnDataBinding(e);
				this.Controls.Clear();
				base.ClearChildState();
				this.CreateControlHierarchy();
				base.ChildControlsCreated = true;
			}

			internal virtual bool MergeTo(object receiver)
			{
				bool _result = false;
				
				if (!this.Descriptor.IsReadOnly) {
					var _value = this.Value;
					
					if (!this.Descriptor.AreEqual(receiver, _value)) {
						this.Descriptor.SetValueFromString(receiver, _value);
						_result = true;
						
						this.DataBindChildren();
					}
				}


				if (this.Descriptor.Converter.GetPropertiesSupported()) {
					var _receiverSubObject = this.Descriptor.GetValue(receiver);
					
					var _anyChildChanged = this.Items.ToList()
						.ConvertAll(_ctl => _ctl.MergeTo(_receiverSubObject))
						.Exists(_merged => _merged);
					//meet structs, enums, etc
					if (this.Descriptor.PropertyType.IsValueType) {
						this.Descriptor.SetValue(receiver, _receiverSubObject);
					}

					if (_anyChildChanged) {
						this.DataBind();
					}

					_result = _result || _anyChildChanged;
				}
				return _result;
			}

			#endregion Methods
		}
	}
}
